#include "Status.h"

Status::Status(){
    _isCopy = false;
    _isModified = true;
}

Status::~Status(){
    while(!files.empty() && !_isCopy){
        delete files.back();
        files.pop_back();
    }
    clearListOfChunks();
}

Status &Status::operator=(const Status &source_status) {
    for (int i = 0; i < (int)source_status.files.size(); i++) {
        this->files.push_back(source_status.files[i]);
    }

    // if we are a copy, do not delete the file vector when destructor is called
    _isCopy = true;
    _isModified = true;

    return *this;
}

bool Status::containsFileChunk(string fileName, int chunk) {
    bool found = false;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            if (files[i]->isChunkPresent(chunk)) {
                found = true;
            }
            break;
        }
    }
    return found;
}

int Status::clearListOfChunks(){
    while(!_listOfChunks.empty() && !_isCopy){
        delete _listOfChunks.back();
        _listOfChunks.pop_back();
    }
    return SAFE;
}

Chunk *Status::createChunk(int fileIndex, string fileName, int chunk, int repLevel){
    Chunk * result = new Chunk();
    result->fileIndex = fileIndex;
    result->fileName = fileName;
    result->chunk = chunk;
    result->repLevel = repLevel;   
    return result;
}

bool listOfChunksSort(Chunk *chunk1, Chunk *chunk2){
    return chunk1->repLevel < chunk2->repLevel;
}

int Status::processListofChunks(){
    DBG_PRINTF(("IS MODIFIED VALUE: %i\n", _isModified));
    if(_isModified){
        // delete previous list
        clearListOfChunks();
        // loop through all the chunks that exists locally of all the files and record the status!
        for(int i = 0; i < (int)files.size(); i++){
            for(int j = 0; j < files[i]->chunks(); j++){
                if(files[i]->isChunkPresent(j+1)){
                    int repLevel = files[i]->chunkReplicationLevel(j+1);
                    Chunk * chunk = createChunk(i, files[i]->fileName(), j+1, repLevel);
                    _listOfChunks.push_back(chunk);
                }
            }
        }
        DBG_PRINTF(("current size: %i\n", (int)_listOfChunks.size()));
        // sort the master list of chunks!
        _listOfChunks.sort(listOfChunksSort);
        _isModified = false;
    }
    return SAFE;
}

int Status::sizeOfListOfChunks(){
    return _listOfChunks.size();
}

int Status::getFromListofChunks(Chunk &chunk){
    int status = errUnknownWarning;
    if(_listOfChunks.empty()){
        status = errNoChunkToShare;
    }else{
        Chunk * get = _listOfChunks.front();
        chunk.fileIndex = get->fileIndex;
        chunk.fileName = get->fileName;
        chunk.chunk = get->chunk;
        chunk.repLevel = get->repLevel;
        _listOfChunks.pop_front();
        _listOfChunks.push_back(get);
        status = SAFE;
    }
    return status;
}

void Status::display(){
    cout << "|" << setw(tableWidth) << "file name"; 
    cout << "|" << setw(tableWidth) << "local (chunks)";
    cout << "|" << setw(tableWidth) << "total (chunks)";
    cout << "|" << setw(tableWidth) << "sys. rep. level";
    cout << "|" << endl;

    cout << setfill('-');

    cout << "|" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "|" << endl;

    for (int i = 0; i < (int)files.size(); i++) {
        File *curFile = files[i];

        cout << setfill(' ');

        cout << "|" << setw(tableWidth) << curFile->fileName();
        cout << "|" << setw(tableWidth) << curFile->localChunks();
        cout << "|" << setw(tableWidth) << curFile->chunks();
        cout << "|" << setw(tableWidth) << curFile->fileReplicationLevel();
        cout << "|" << endl;

        cout << setfill('-');

        cout << "|" << setw(tableWidth) << "-";
        cout << "+" << setw(tableWidth) << "-";
        cout << "+" << setw(tableWidth) << "-";
        cout << "+" << setw(tableWidth) << "-";
        cout << "|" << endl;
    }

    cout << setfill(' ');
}

int Status::receivedFileChunk(string fileName, int chunk) {
    int status = SAFE;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            files[i]->receivedChunk(chunk);
            DBG_PRINTF(("NEW LOCAL CHUNK in file %s\n", fileName.c_str()));
            break;
        }
    }
    _isModified = true;
    return status;
}

int Status::receivedSystemChunk(string fileName, int chunk){
    int status = SAFE;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            files[i]->successTransfer(chunk);
            DBG_PRINTF(("NEW SYSTEM CHUNK in file %s\n", fileName.c_str()));
            break;
        }
    }   
    _isModified = true;
    return status;
}

int Status::removeSystemChunk(string fileName, int chunk){
    int status = SAFE;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            files[i]->decreaseSystemChunk(chunk);
            DBG_PRINTF(("removed SYSTEM CHUNK in file %s\n", fileName.c_str()));
            if(files[i]->systemChunks() == 0){
                files.erase(files.begin()+i);
                DBG_PRINTF(("removed file %s since 0 chunks in system\n", fileName.c_str()));
            }
            break;
        }
    }   
    _isModified = true;
    return status;
}

bool Status::containsFile(string fileName){
    bool found = false;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            found = true;
            break;
        }
    }
    return found;
}

int Status::leaveNetwork(){
    for(unsigned int i = 0; i < files.size(); i++){
        files[i]->revertToLocalData();
    }
    _isModified = true;
    return SAFE;
}

int Status::removeNonLocalFiles(){
    unsigned int i = 0 ;
    while(i < files.size()){
        if(!files[i]->hasAnyChunk()){
            files.erase(files.begin()+i);
        }else{
            i++;
        }
    }
    _isModified = true;
    return SAFE;    
}

bool Status::isFileComplete(string fileName, int&chunk){
    bool complete = false;
    for(unsigned int i = 0; i < files.size(); i++){
        if(files[i]->fileName() == fileName){
            complete = files[i]->isComplete();
            chunk = files[i]->chunks();
            break;
        }
    }
    return complete;
}

int Status::addFile(string fileName, int chunks, bool complete){
    int status = errUnknownWarning;
    if (!containsFile(fileName)) {
        File *newFile = new File(fileName, chunks);
        files.push_back(newFile);
        _isModified = true;
        if(complete){ 
            newFile->completeFile();
        }
        status = SAFE;
    } else {
        status = errFileAlreadyExist;
    }
    return status;
}


